package remote

import (
	"deploy-tools/utils"
	"errors"
	"fmt"
	"github.com/pkg/sftp"
	uuid "github.com/satori/go.uuid"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// SendFile 这里应该要处理session断开的流程吧
func (n *Client) SendFile(src string, remote string) (string, error) {
	client, err := n.Client()
	if err != nil {
		return "", err
	}
	session, err := sftp.NewClient(client)
	if err != nil {
		return "", err
	}
	defer session.Close()
	info, err := os.Stat(src)
	if err != nil {
		return "", err
	}
	var source string
	var filename string
	var isTar = false
	var dest = remote
	var remoteDir string
	if info.IsDir() {
		filename = uuid.NewV4().String() + ".tar.gz"
		tempDir := filepath.Join(os.TempDir(), "deploy-tools")
		os.MkdirAll(tempDir, os.ModePerm)
		temp := filepath.Join(tempDir, filename)
		err = utils.TarXz(temp, src)
		if err != nil {
			return "", err
		}
		source = temp
		isTar = true
		dest = "/tmp/" + filename
		remoteDir = remote
		defer os.Remove(temp)
	} else {
		source = src
		filename = info.Name()
		if strings.HasSuffix(remote, "/") {
			remoteDir = remote
			dest = remote + filename
		} else {
			remoteDir = filepath.Dir(dest)
		}
	}
	remoteDir = strings.ReplaceAll(remoteDir, "\\", "/")
	err = session.MkdirAll(remoteDir)
	if err != nil {
		return "", err
	}
	dstFile, err := session.Create(dest)
	if err != nil {
		return "", err
	}
	defer dstFile.Close()

	srcFile, err := os.Open(source)
	if err != nil {
		return "", err
	}
	defer srcFile.Close()

	l, err := io.Copy(dstFile, srcFile)
	if err != nil {
		fmt.Printf("sendFile error with: %v\n", err)
		return "", err
	}
	fmt.Printf("sendFile ok with size: %.2fK\n", float64(l)/(1024))
	if isTar {
		_, err = n.Execute(fmt.Sprintf("tar -xzf %s -C %s", dest, remoteDir))
		return remoteDir, err
	}
	return dest, nil
}

// FetchFile 从远程服务器拉取文件到本地指定路径
func (n *Client) FetchFile(src string, local string, override bool) (string, error) {
	session, err := sftp.NewClient(n.client)
	if err != nil {
		return "", err
	}
	defer session.Close()
	info, err := session.Stat(src)
	if err != nil {
		return "", err
	}
	result := ""
	if info.IsDir() {
		result, err = FetchDir(n, session, src, local, override)
	} else {
		result, err = FetchFile(session, src, local, override)
	}
	return result, err
}

// FetchDir 从远程服务器拉取文件夹到本地指定路径，已经确定远程是文件夹, local一定是文件夹，不可能是文件
func FetchDir(n *Client, session *sftp.Client, src string, local string, override bool) (string, error) {

	localInfo, err := os.Stat(local)

	if err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(local, 0755)
		}
	} else {
		if !localInfo.IsDir() {
			return "", errors.New("本地文件已存在且本地路径不是文件夹！")
		}
	}
	if err != nil {
		return "", err
	}

	tempFilename := uuid.NewV4().String() + ".tar.gz"
	tempDir := "/tmp/deploy-tools"
	err = session.MkdirAll(tempDir)
	if err != nil {
		return "", err
	}
	tempPath := tempDir + "/" + tempFilename
	_, err = n.Execute(fmt.Sprintf("tar -czf %s %s", tempPath, src))
	if err != nil {
		return "", err
	}

	srcFile, err := session.Open(tempPath)
	if err != nil {
		return "", err
	}
	defer srcFile.Close()
	localTempPath := filepath.Join(local, tempFilename)
	localFile, err := os.Create(localTempPath)
	if err != nil {
		return "", err
	}
	defer localFile.Close()
	_, err = io.Copy(srcFile, localFile)
	if err != nil {
		return "", err
	}
	_ = localFile.Close()
	_ = srcFile.Close()
	err = utils.UnTarXz(local, localTempPath)
	if err != nil {
		return "", err
	}
	_ = os.Remove(localTempPath)
	return local, nil
}

// FetchFile 从远程服务器拉取文件到本地指定路径，已经确定远程是文件,而非文件夹, local可能是文件，也有可能是文件夹
// 只支持从linux服务器拉取文件
func FetchFile(session *sftp.Client, src string, local string, override bool) (string, error) {
	srcFile, err := session.Open(src)
	if err != nil {
		return "", err
	}
	defer srcFile.Close()

	filename := src[strings.LastIndex(src, "/")+1:]

	dstPath := local
	info, err := os.Stat(local)
	if err != nil && !os.IsNotExist(err) {
		return "", err
	} else if err != nil {
		last := local[len(local)-1]
		if os.IsPathSeparator(last) {
			dstPath = filepath.Join(local, filename)
		} else {
			dstPath = local
		}
	} else if info.IsDir() {
		dstPath = filepath.Join(local, filename)
	} else if !override {
		return "", errors.New(fmt.Sprintf("本地文件:%s 已存在！", local))
	}
	dstPath, err = filepath.Abs(dstPath)
	if err != nil {
		return "", err
	}
	parent := filepath.Dir(dstPath)
	_ = os.MkdirAll(parent, os.ModePerm)

	dstFile, err := os.Create(dstPath)
	if err != nil {
		return "", err
	}
	defer dstFile.Close()

	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return "", err
	}
	return dstPath, nil
}
